package ci.web.router;

import io.netty.util.internal.ConcurrentSet;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import ci.web.core.CiContext;
import ci.web.util.CiClassLoader;
import ci.web.util.ClassUtil;

/**
 * ci-handler扫描器<br/>
 * 匹配url-脚本
 * @author zhh
 */
public class CiScaner {

    private static InternalLogger logger = InternalLoggerFactory.getInstance(CiScaner.class);
    
    //脚本-常规路径
    private Map<String, CiHandler> map;
    //脚本-有路径做为参数
    private Set<CiHandler> paths;
    //脚本-动态添加类
    private Set<String> handlerClass;
    
    protected ClassLoader loader;
    //扫描路径
    protected String dir;
    //扫描限定包
    protected String packageName;
    
    public CiScaner(){
        map = new ConcurrentHashMap<>();
        handlerClass = new ConcurrentSet<>();
        paths = new ConcurrentSet<>();
    }
    public CiScaner(String dir, String packageName, Set<String> classes) {
        this.dir = dir;
        this.packageName = packageName;
        map = new ConcurrentHashMap<>();
        paths = new ConcurrentSet<>();
        CiClassLoader loader = new CiClassLoader(packageName, this.getClass().getClassLoader());
        this.loader = loader;
        if(dir!=null && dir.length()>0){
            try {
                String[] arr = dir.split("[;,]");
                for(String path : arr) {
                    path = path.trim();
                    if(path.isEmpty()){
                        continue;
                    }
                    loader.addPath(new File(path).toURI().toURL());
                }
                scanTo(loader, packageName, map, paths);
            } catch (IOException e) {
                logger.error("", e);
            }
        }
        this.handlerClass = new ConcurrentSet<>();
        add(classes);
    }
    /**
     * 扫描路径
     * @return
     */
    public String dir() {
        return dir;
    }
    /**
     * 搜索类起始包路径
     * @return
     */
    public String packageName() {
        return packageName;
    }
    /**
     * 搜索类路径URL
     * @return
     */
    public URL[] urls() {
        if(loader instanceof CiClassLoader){
            return ((CiClassLoader)loader).getURLs();
        }
        return null;
    }
    
    /**
     * 直接添加路由实现类
     * @param clazz
     */
    public void add(Class<?> clazz){
        this.handlerClass.add(clazz.getName());
        scanRouterTo(clazz, map, paths);
    }
    /**
     * 直接添加路由实现类
     * @param classes
     */
    public void add(Set<String> classes){
        if(classes==null)return;
        for(String className : classes){
            try {
                scanRouterTo(ClassUtil.loadClass(className), map, paths);
            } catch (Exception e) {
                handlerClass.remove(className);
                logger.error("", e);
            }
        }
    }
    
    /**
     * 直接添加的路由实现类-类名集合
     * @return
     */
    public Set<String> handlers(){
        return handlerClass;
    }
    
    /**
     * class加载器
     * @return
     */
    public ClassLoader loader(){
        return loader;
    }
    
    /**
     * 匹配病获取ctx的CiHandler
     * @param ctx
     * @return
     */
    public CiHandler get(CiContext ctx) {
        String path = ctx.path().toLowerCase();
        CiHandler p = map.get(path);
        if(p==null){
            for(CiHandler node : paths){
                if(path.startsWith(node.path)){
                    p = node;
                    break;
                }
            }
        }
        return p;
    }
    
    /**
     * 扫描
     * @param loader
     * @param packageName
     * @param map
     */
    public static void scanTo(ClassLoader loader, String packageName, Map<String, CiHandler> map, Set<CiHandler> paths) {
        try {
            Set<Class<?>> classes = ClassUtil.getAllClass(loader, packageName);
            for (Class<?> clazz : classes) {
                scanRouterTo(clazz, packageName, map, paths);
            }
        } catch (Exception e) {
            logger.debug("scan", e);
        }
    }
    /**
     * 扫描
     * @param clazz
     * @param map
     */
    public static void scanRouterTo(Class<?> clazz, Map<String, CiHandler> map, Set<CiHandler> paths) {
        scanRouterTo(clazz, clazz.getPackage().getName(), map, paths);
    }
    /**
     * 扫描
     * @param clazz
     * @param packageName
     * @param map
     */
    public static void scanRouterTo(Class<?> clazz, String packageName, Map<String, CiHandler> map, Set<CiHandler> paths) {
        try{
        	CiHandler constructorHandler = CiConstructorHandler.tryMake(clazz, packageName);
            if(constructorHandler!=null){
                addRouterTo(constructorHandler, map, paths);
            }
            if(CiHandler.check(clazz)){
                for (Method method : clazz.getDeclaredMethods()) {
                    if (!Modifier.isPublic(method.getModifiers())
                        && method.getParameterCount()>0    
                            ) {
                        continue;
                    }
                    if(CiHandler.check(method)){
                        addRouterTo(new CiHandler(packageName, method), map, paths);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("scan", e);
        }
    }
    private static void addRouterTo(CiHandler wrap, Map<String, CiHandler> map, Set<CiHandler> paths) {
    	if(wrap.isPathMatch()){
    		paths.add(wrap);
    		logger.debug("add-router/*:{}", wrap);
    		return;
    	}
        if(map.containsKey(wrap.path)){
            logger.warn("add-router-repeat:{}", wrap);
        }else{
            map.put(wrap.path, wrap);
            if(wrap.path.charAt(wrap.path.length()-1)=='/'){
            	map.put(wrap.path+"index", wrap);
            }
            logger.debug("add-router:{}", wrap);
        }        
    }
    
    
}
